package com.fish.common.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 图形验证码服务
 *
 * @author fish
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CaptchaService {

    private final StringRedisTemplate stringRedisTemplate;

    private static final String CAPTCHA_PREFIX = "captcha:";
    private static final int CAPTCHA_WIDTH = 120;
    private static final int CAPTCHA_HEIGHT = 40;
    private static final int CAPTCHA_LENGTH = 4;
    private static final long CAPTCHA_EXPIRE_TIME = 5; // 分钟

    // 验证码字符集（去除易混淆字符）
    private static final String CAPTCHA_CHARS = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";

    /**
     * 生成验证码
     *
     * @return 验证码信息（包含key和base64图片）
     */
    public CaptchaVO generateCaptcha() {
        // 生成唯一key
        String key = UUID.randomUUID().toString().replace("-", "");
        
        // 生成随机验证码文本
        String code = generateCode();
        
        // 保存到Redis，5分钟过期
        stringRedisTemplate.opsForValue().set(
            CAPTCHA_PREFIX + key,
            code,
            CAPTCHA_EXPIRE_TIME,
            TimeUnit.MINUTES
        );
        
        // 生成验证码图片
        String base64Image = generateImage(code);
        
        log.info("生成验证码成功，key: {}, code: {}", key, code);
        
        return new CaptchaVO(key, base64Image);
    }

    /**
     * 验证验证码
     *
     * @param key  验证码key
     * @param code 用户输入的验证码
     * @return 是否验证成功
     */
    public boolean verifyCaptcha(String key, String code) {
        if (key == null || code == null) {
            log.warn("验证码key或code为空");
            return false;
        }

        String redisKey = CAPTCHA_PREFIX + key;
        String savedCode = stringRedisTemplate.opsForValue().get(redisKey);

        if (savedCode == null) {
            log.warn("验证码不存在或已过期，key: {}", key);
            return false;
        }

        // 不区分大小写比较
        boolean isValid = savedCode.equalsIgnoreCase(code.trim());

        if (isValid) {
            // 验证成功后删除验证码（一次性使用）
            stringRedisTemplate.delete(redisKey);
            log.info("验证码验证成功，key: {}", key);
        } else {
            log.warn("验证码不匹配，key: {}, 期望: {}, 实际: {}", key, savedCode, code);
        }

        return isValid;
    }

    /**
     * 生成随机验证码文本
     */
    private String generateCode() {
        Random random = new Random();
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < CAPTCHA_LENGTH; i++) {
            code.append(CAPTCHA_CHARS.charAt(random.nextInt(CAPTCHA_CHARS.length())));
        }
        return code.toString();
    }

    /**
     * 生成验证码图片
     */
    private String generateImage(String code) {
        BufferedImage image = new BufferedImage(CAPTCHA_WIDTH, CAPTCHA_HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();
        Random random = new Random();

        // 设置背景色
        g.setColor(new Color(240, 240, 240));
        g.fillRect(0, 0, CAPTCHA_WIDTH, CAPTCHA_HEIGHT);

        // 绘制干扰线
        for (int i = 0; i < 20; i++) {
            int x1 = random.nextInt(CAPTCHA_WIDTH);
            int y1 = random.nextInt(CAPTCHA_HEIGHT);
            int x2 = random.nextInt(CAPTCHA_WIDTH);
            int y2 = random.nextInt(CAPTCHA_HEIGHT);
            g.setColor(getRandomColor(160, 200));
            g.drawLine(x1, y1, x2, y2);
        }

        // 绘制验证码字符
        g.setFont(new Font("Arial", Font.BOLD, 28));
        for (int i = 0; i < code.length(); i++) {
            // 随机颜色
            g.setColor(getRandomColor(20, 130));
            // 随机角度
            int degree = random.nextInt(30) - 15;
            // 计算位置
            int x = 15 + i * 25;
            int y = 28;
            // 旋转并绘制
            g.rotate(Math.toRadians(degree), x, y);
            g.drawString(String.valueOf(code.charAt(i)), x, y);
            g.rotate(-Math.toRadians(degree), x, y);
        }

        // 绘制干扰点
        for (int i = 0; i < 100; i++) {
            int x = random.nextInt(CAPTCHA_WIDTH);
            int y = random.nextInt(CAPTCHA_HEIGHT);
            g.setColor(getRandomColor(150, 200));
            g.fillRect(x, y, 1, 1);
        }

        g.dispose();

        // 转换为Base64
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            ImageIO.write(image, "png", baos);
            byte[] bytes = baos.toByteArray();
            return "data:image/png;base64," + Base64.getEncoder().encodeToString(bytes);
        } catch (IOException e) {
            log.error("生成验证码图片失败", e);
            throw new RuntimeException("生成验证码失败");
        }
    }

    /**
     * 获取随机颜色
     */
    private Color getRandomColor(int min, int max) {
        Random random = new Random();
        int r = min + random.nextInt(max - min);
        int g = min + random.nextInt(max - min);
        int b = min + random.nextInt(max - min);
        return new Color(r, g, b);
    }

    /**
     * 验证码VO
     */
    public static class CaptchaVO {
        private String key;
        private String image;

        public CaptchaVO() {
        }

        public CaptchaVO(String key, String image) {
            this.key = key;
            this.image = image;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public String getImage() {
            return image;
        }

        public void setImage(String image) {
            this.image = image;
        }
    }
}
